#pragma once

#include <vex/iterable/range_base.h>
#include <vex/iterable/range_state.h>
#include <vex/iterable/adapter/adapter_default.h>
#include <vex/iterable/adapter/adapter_access.h>
#include <vex/iterable/stl_interop/stl_convert_traits.h>
#include <vex/iterable/stl_interop/stl_range_from_iterator_adapter_body.h>
#include <iterator>

namespace vex { namespace iterable { namespace stl_interop {

    template< class TFinal, class TItr, class TItrTraits = std::iterator_traits<TItr> >
    class stl_range_from_iterator_base : public range_base<
        typename convert_iterator_traits<TItr, TItrTraits>::category,
        typename convert_iterator_traits<TItr, TItrTraits>::reference,
        adapter::adapter_default< // this inherits a proper subset of iterable methods
            TFinal, 
            typename stl_interop::convert_iterator_traits<TItr, TItrTraits>::category, 
            typename stl_interop::convert_iterator_traits<TItr, TItrTraits>::reference
        >
    > 
    {
    protected:
        typedef typename stl_interop::convert_iterator_traits<TItr, TItrTraits>::category cat_t;
        typedef typename stl_interop::convert_iterator_traits<TItr, TItrTraits>::reference ref_t;

        typedef stl_range_from_iterator_adapter<TFinal, TItr, cat_t, ref_t> adaptee_type;
    
        stl_range_from_iterator_base(adaptee_type const& p_adaptee_body)
            : m_adaptee(p_adaptee_body) {}

        // construct from iterator iterable and state
        template<class UItr>
        stl_range_from_iterator_base(UItr && p_beg, UItr && p_end, 
            range_state const & p_state = range_state::not_started)
            : m_adaptee(std::forward<UItr>(p_beg), std::forward<UItr>(p_end), p_state) {}

        // construct from single iterator which must be of output iterator category
        template<class UItr>
        explicit stl_range_from_iterator_base(UItr && p_beg, std::output_iterator_tag const &)
            : m_adaptee(std::forward<UItr>(p_beg)) {}

        template<class UFinal, class UItr, class UItrTraits>
        stl_range_from_iterator_base(stl_range_from_iterator_base<UFinal, UItr, UItrTraits> const & p_other)
            : m_adaptee(p_other.m_adaptee) {}

        stl_range_from_iterator_base(stl_range_from_iterator_base && p_other)
            : m_adaptee(std::move(p_other.m_adaptee)) 
        {}

        ~stl_range_from_iterator_base() {}
    private:
        template <class, class, class> friend class stl_range_from_iterator_base;
        friend class adapter::adapter_access;

        adaptee_type m_adaptee;
        adaptee_type& get_adaptee_ref() { return m_adaptee; }
        adaptee_type const & get_adaptee_ref() const { return m_adaptee; }

    public:
        void swap(TFinal& p_other)
        {
            m_adaptee.swap(static_cast<stl_range_from_iterator_base&>(p_other).m_adaptee);
            //m_adaptee.swap(p_other.m_adaptee);
        }
    };
}}}